#pragma once
#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/time.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/stat.h>
#include<cstdio>
#include<cstring>
#include<fcntl.h>
#include<signal.h>
#include<pthread.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include <sys/sendfile.h> 
#include<netinet/in.h>
#include<vector>
#include<arpa/inet.h>
#include<sys/wait.h>
#include<functional>
#include <semaphore.h>
#include<jsoncpp/json/json.h>
#include<sys/select.h>
#include<unordered_map>
#include"Until.hpp"
#define OK 200
#define NOT_FOUND 404
#define WEB_ROOT "./wwwroot"    //根目录
#define HOME_PAGE "index.html"  //首页
#define Error_404_PAGE "./wwwroot/404.html"
#define LINE_END "\r\n" 
using namespace std;
static string Code2Desc(int code)   //构建状态码描述
{
    string dec;
    switch (code)
    {
    case 200:
        dec = "OK";
        break;
    case 404:
        dec = "NOT FOUND";
        break;
    default:
        break;
    }
    return dec;
}
class HttpRequest
{
public:
    string request_line;
    vector<string> request_header;
    string blank;
    string request_body;
    string uri;
    string method;
    string version;
    unordered_map<string,string>header_kv;
    long long content_length;  //正文长度--只有POST方法才有
    string path;    //访问路径
    string query_string;    //参数
    bool cgi=false;
};

class HttpResponse
{
public:
    string status_line;
    vector<string> response_header;
    string blank;
    string response_body;
    int code=OK;
    int fd = -1;                    //非cgi机制下用户所需资源的文件描述符
    int size=0;             //静态网页信息的大小
    string suffix;    //访问资源后缀
};

class EndPoint
{
private:
    HttpRequest http_request;
    HttpResponse http_response;
    int sock;
public:
    EndPoint(int Sock)
    :sock(Sock){}
    ~EndPoint(){}
    void RevHttpRequestLine()
    {
       Util::ReadLine(sock,http_request.request_line);
       http_request.request_line.pop_back();
    }
    void RecvHttpRequestHeader()
    {
        string line;
        while (1)
        {
            line.clear();
            Util::ReadLine(sock, line);
            if(line=="\n")
            {
                http_request.blank=line;  
                break;
            }
            line.pop_back();    //将每行末尾的"\n"字符去除
            http_request.request_header.push_back(line);
        }
    }
    void ParseHttpRequestLine()
    {
        Util::DivideThreeLine(http_request.request_line,http_request.method,http_request.version,http_request.uri,' ');

    }
    void ParseHttpRequestHeader()
    {
        for(auto&e:http_request.request_header)
        {
            Util::DivideKV(e,http_request.header_kv,": ");
        }
    }
    bool IsNeedRecvHttpRequestBody()
    {
        if(http_request.method=="POST")
        {
            if(http_request.header_kv.find("Content-Length")!=http_request.header_kv.end())
            {
                int length=stoi(http_request.header_kv["Content-Length"]);
                http_request.content_length=length;
                return true;
            }
        }
        return false;
    }
    void RecvHttpRequestBody()
    {
        if(IsNeedRecvHttpRequestBody())
        {   
            int content_length=http_request.content_length;
            while(content_length--)
            {
                char ch='x';
                int n=recv(sock,&ch,1,0);
                if(n>0)
                {
                    http_request.request_body.push_back(ch);    //post方法读取正文内容到http_request.request_body中
                }
                else
                    break;
            }
        }
    }

    //功能总体架构
    void RevHttpRequest()
    {
        RevHttpRequestLine();
        RecvHttpRequestHeader();
    }
    void ParseHttpRequest()
    {
        ParseHttpRequestLine();
        ParseHttpRequestHeader();
        
    }
    int ProcessCgi()
    {
        string bin=http_request.path;  //bin为可执行程序路径
        //创建子进程通过进程切换执行可执行程序,通过管道进行父进程和可执行程序的交互
        //这是站在父进程角度创建的管道input和output
        int input[2];   //父进程往input[0]进行读取
        int output[2];  //父进程往output[1]进行写入
        if(pipe(input)<0)
        {
            LOG(ERROR,"pipe create error");
            return 404;
        }
        if(pipe(output)<0)
        {
            LOG(ERROR,"pipe create error");
            return 404;
        }
        pid_t id=fork();
        if(id==0)
        {
            cout<<http_request.query_string<<endl;
            close(input[0]);
            close(output[1]);
            dup2(output[0],0);  //对子进程进行管道重定向0号和1号文件描述符
            dup2(input[1],1);

            string method;
            if (http_request.method == "GET") // GET方法向可执行程序通过环境变量的方式发送参数和方法
            {
                method = "METHOD=GET";
                string query_string = "QUERY_STRING=" + http_request.query_string;
                putenv((char *)query_string.c_str());
            }
            else
            {
                method = "METHOD=POST";
                string content_length_env="CONTENT_LENGTH="+to_string(http_request.content_length);
                putenv((char*)content_length_env.c_str());
            }
            putenv((char *)method.c_str());
            execl(bin.c_str(), bin.c_str(), nullptr); // 子进程切换到可执行程序
            exit(0);
        }
        else if (id < 0)
        {
            LOG(ERROR, "fork error");
            return 404;
        }
        else
        {
            close(input[1]);
            close(output[0]);
            if(http_request.method=="POST") //POST方法向可执行程序通过管道发送请求正文的内容
            {
                string body_string=http_request.request_body;
                write(output[1],body_string.c_str(),body_string.size());
            }

            char ch='x';    //填充响应报文数据
            while(read(input[0],&ch,1)>0)
            {   
                http_response.response_body.push_back(ch);
            }

            int status=0;
            pid_t ret=waitpid(id,&status,0);
            if(ret==id)     //检测可执行程序是否正常退出
            {
                if(WIFEXITED(status))
                {
                    if(WEXITSTATUS(status))
                    http_response.code=OK;
                    else
                    http_response.code=404;
                }                
                else
                {
                    http_response.code=404;
                }
            }
            close(input[0]);    //记得关闭不使用的文件描述符
            close(output[1]);
        }
    }
    int ProcessNoCgi()
    {
        http_response.fd = open(http_request.path.c_str(), O_RDONLY);
        if (http_response.fd >= 0)
        {
            http_response.status_line = http_request.version;  //添加状态行信息(版本+状态码+状态码描述)
            http_response.status_line += " ";
            http_response.status_line += to_string(http_response.code);
            http_response.status_line += " ";
            http_response.status_line += Code2Desc(http_response.code);
            http_response.status_line += LINE_END;
            //填充响应报头信息
            string content_length="Content-Length: "+to_string(http_response.size)+"\r\n";
            string suffix_Mes="Cotent-Type: "+http_response.suffix+" "+Util::Suffix2Desc(http_response.suffix)+
            "\r\n";
            http_response.response_header.push_back(content_length);
            http_response.response_header.push_back(suffix_Mes);
            return OK;
        }
       
        return NOT_FOUND;   //fd不存在修改错误码为404
       
    }
    void BuildHttpHelper(int code)      //处理当错误码为不为OK的情况
    {
        string& status_line=http_response.status_line;
        status_line+=http_request.version;
        status_line+=" ";
        status_line+=to_string(code);
        status_line+=" ";
        status_line+=Code2Desc(code);
        status_line += LINE_END;
        string error_path;
        switch (code)   //根据错误码返回对应页面
        {
        case 404:
            error_path = Error_404_PAGE;
            break;
        default:
            break;
        }
        http_response.fd = open(error_path.c_str(), O_RDONLY);
        string head_line="Content-Type: ";
        head_line+=".html ";
        head_line+="text/html\r\n";
        http_response.response_header.push_back(head_line);
        head_line="Content-Length: ";
        struct stat st;
        stat(error_path.c_str(),&st);
        http_response.size=st.st_size;
        head_line+=to_string(http_response.size);
        head_line+="\r\n";
        http_response.response_header.push_back(head_line);
    }
    void BuildHttpResponse()
    {
        RecvHttpRequestBody();
        if (http_request.method != "GET" && http_request.method != "POST")
        {
            http_response.code = NOT_FOUND;
            http_request.cgi=false;
        }
        if (http_request.method == "GET")
        {
            if (http_request.uri.find('?') == string::npos)
            {
                http_request.path = http_request.uri;
                http_request.cgi = false;
            }
            else
            {
                Util::CutTwoLine(http_request.uri, http_request.path, http_request.query_string, "?");
                http_request.cgi = true; // 带参的GET或者POST都用CGI机制进行处理
            }
        }
        else if(http_request.method == "POST")
        {
            http_request.cgi = true;
            http_request.path = http_request.uri;
        }

        string &path = http_request.path; // 加上根目录
        path = WEB_ROOT + path;
        if (path.back() == '/')
        {
            path += HOME_PAGE;
        }

        struct stat st;
        if (stat(http_request.path.c_str(), &st) == 0)  //判断文件是否存在
        {
            if (S_ISDIR(st.st_mode)) // 判断路径是否为目录
            {
                http_request.path+="/";
                http_request.path+=HOME_PAGE;
                stat(http_request.path.c_str(),&st);
            }
            if((st.st_mode&S_IXUSR) || (st.st_mode&S_IXGRP) || (st.st_mode&S_IXOTH))
            {
                http_request.cgi=true;
            }
            http_response.size=st.st_size;
        }
        else    //路径不存在的情况
        {
            string info=http_request.path;
            info+=" NOT FOUND";
            LOG(WARNING,info);
            http_response.code=NOT_FOUND;
            http_request.cgi = false;
        }

        http_response.suffix = Util::SearchSuffix(http_request.path); // 表明后缀类型

        if (http_response.code != OK)
        {
            BuildHttpHelper(http_response.code);
        }
        else
        {
            if (http_request.cgi)
            {
                ProcessCgi();
            }
            else
            {
                ProcessNoCgi();
            }
        }
    }
    
    void SendHttpResponse()
    {
        
        send(sock, http_response.status_line.c_str(), http_response.status_line.size(), 0);
        for (auto &iter : http_response.response_header)
        {
            send(sock, iter.c_str(), iter.size(), 0);

        }
        send(sock,http_request.blank.c_str(),http_request.blank.size(),0);
        if (http_request.cgi)
        {
           send(sock,http_response.response_body.c_str(),http_response.response_body.size(),0); //一次发送的数据量小的话直接这样写即可
        }
        else
        {
        sendfile(sock, http_response.fd, nullptr, http_response.size); // 发送正文内容(非cgi机制才需要)
        }
        close(http_response.fd);
    }
};

class Callback
{
public:
    void operator()(int sock)
    {
        HandlerRequest(sock);
    }
   void HandlerRequest(int sock)
    {
        EndPoint* ed=new EndPoint(sock);
        ed->RevHttpRequest();
        ed->ParseHttpRequest();
        ed->BuildHttpResponse();
        ed->SendHttpResponse();
        delete ed;
    }
};